package redislib

import (
	"context"
	"net"
	"runtime"
	"time"

	"gitee.com/night-tc/gobige/common"
	"gitee.com/night-tc/gobige/logger"

	"github.com/redis/go-redis/v9"
)

var (
	// moduleCap 用于设置默认的协程数，通常为 CPU 核心数的 10 倍
	moduleCap = runtime.NumCPU() * 10
)

// RedisSetAddr 设置 Redis 的地址，带端口
func RedisSetAddr(addr string) redisoptions {
	return func(mod *RedisAccess) {
		mod.addr = addr
	}
}

// RedisSetAuth 设置 Redis 的认证密码
func RedisSetAuth(auth string) redisoptions {
	return func(mod *RedisAccess) {
		mod.auth = auth
	}
}

// RedisSetIndexDB 设置 Redis 默认的数据库索引
func RedisSetIndexDB(indexdb int) redisoptions {
	return func(mod *RedisAccess) {
		mod.indexdb = indexdb
	}
}

// RedisSetMaxIdle 设置 Redis 的最大空闲连接数
// 表示即使没有 Redis 连接时依然可以保持 N 个空闲的连接，而不被清除，随时处于待命状态
func RedisSetMaxIdle(maxidle int) redisoptions {
	return func(mod *RedisAccess) {
		mod.maxIdle = maxidle
	}
}

// RedisSetMaxActive 设置 Redis 的最大激活连接数
// 表示同时最多有 N 个连接，为 0 时表示没有限制
func RedisSetMaxActive(maxactive int) redisoptions {
	return func(mod *RedisAccess) {
		mod.maxActive = maxactive
	}
}

// RedisSetIdleTimeout 设置 Redis 的最大空闲连接等待时间
// 超过此时间后，空闲连接将被关闭（秒）
func RedisSetIdleTimeout(idleTimeout time.Duration) redisoptions {
	return func(mod *RedisAccess) {
		mod.idleTimeout = idleTimeout * time.Second
	}
}

// RedisSetErrRetry 设置出错重试次数，至少为 1
func RedisSetErrRetry(errRetry int) redisoptions {
	return func(mod *RedisAccess) {
		if errRetry > 1 {
			mod.errRetry = errRetry
		}
	}
}

// RedisSeterrFunc 设置错误处理回调函数
func RedisSeterrFunc(errF errFunc) redisoptions {
	return func(mod *RedisAccess) {
		mod.errFunc = errF
	}
}

// redisoptions 是一个函数类型，用于配置 RedisAccess 的选项
type redisoptions func(mod *RedisAccess)

// errFunc 是错误处理回调函数类型
// isend = true 表示重试也失败了，最后一次的回调
// isend = false 表示后面还可以继续重试
// 返回值 true 表示如果可以，继续重试
type errFunc func(err error, isend bool, commandName string, args []interface{}) bool

// RedisAccess 是 Redis 管理器
type RedisAccess struct {
	DBConobj    *redis.Client // Redis 连接池
	addr        string        // 连接字符串
	indexdb     int           // 默认数据库号
	auth        string        // 连接密码
	maxIdle     int           // 最大空闲连接数
	maxActive   int           // 最大激活连接数
	idleTimeout time.Duration // 最大空闲连接等待时间
	errRetry    int           // 出错重试次数
	errFunc     errFunc       // 错误处理回调函数
}

// NewRedisAccess 生成新的 Redis 管理器
func NewRedisAccess(opts ...redisoptions) *RedisAccess {
	result := &RedisAccess{
		addr:        "127.0.0.1:6379",
		auth:        "",
		maxIdle:     moduleCap / 10,
		maxActive:   moduleCap,
		idleTimeout: 3600 * time.Second,
		errRetry:    1,
		errFunc:     nil,
	}
	for _, opt := range opts {
		opt(result)
	}
	result.DBConobj = redis.NewClient(&redis.Options{
		Addr: result.addr,
		Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
			logger.Debugf(logger.LogKey_Redis, "redis dial.%s", addr)
			netDialer := &net.Dialer{
				Timeout:   5 * time.Second,
				KeepAlive: 5 * time.Minute,
			}
			return netDialer.DialContext(ctx, network, addr)
		},
		OnConnect: func(ctx context.Context, cn *redis.Conn) error {
			logger.Debugf(logger.LogKey_Redis, "redis OnConnect.%s", cn)
			return nil
		},
		Password:        result.auth,
		DB:              result.indexdb,
		PoolSize:        result.maxActive,
		DialTimeout:     100 * time.Second,
		PoolTimeout:     100 * time.Second,
		ReadTimeout:     -1,
		WriteTimeout:    -1,
		ConnMaxIdleTime: result.idleTimeout,
	})
	return result
}

// GetConn 获取一个可用的 Redis 连接
// 使用完后应调用 defer conn.Close() 将连接放回池中
func (access *RedisAccess) GetConn() *RedisHandleModel {
	return &RedisHandleModel{
		Client: access.DBConobj,
		access: access,
		Ctx:    context.Background(),
	}
}

// Close 关闭 Redis 连接池，通常在关闭服务器时调用
func (access *RedisAccess) Close() {
	access.DBConobj.Close()
}

// RedisHandleModel 封装了 Redis 的一些常用命令
type RedisHandleModel struct {
	*redis.Client
	Ctx    context.Context
	access *RedisAccess
}

// HgetallByStringMap 获取 Redis 哈希表中的所有字段和值，返回字符串类型的 map
func (rd *RedisHandleModel) HgetallByStringMap(key string) (result map[string]string, reserr error) {
	tmpli := make([]string, 0, 10)
	iter := rd.HScan(rd.Ctx, key, 0, "", 10).Iterator()
	for iter.Next(rd.Ctx) {
		tmpli = append(tmpli, iter.Val())
	}
	result = make(map[string]string)
	for i := 0; i < len(tmpli); i += 2 {
		result[tmpli[i]] = tmpli[i+1]
	}
	return result, nil
}

// HgetallByBytesMap 获取 Redis 哈希表中的所有字段和值，返回字节切片类型的 map
func (rd *RedisHandleModel) HgetallByBytesMap(key string) (result map[string][]byte, reserr error) {
	tmpli := make([]string, 0, 10)
	iter := rd.HScan(rd.Ctx, key, 0, "", 10).Iterator()
	for iter.Next(rd.Ctx) {
		tmpli = append(tmpli, iter.Val())
	}
	result = make(map[string][]byte)
	for i := 0; i < len(tmpli); i += 2 {
		result[tmpli[i]] = []byte(tmpli[i+1])
	}
	return result, nil
}

// HgetallByInt64Map 获取 Redis 哈希表中的所有字段和值，返回 int64 类型的 map
func (rd *RedisHandleModel) HgetallByInt64Map(key string) (result map[string]int64, reserr error) {
	tmpli := make([]string, 0, 10)
	iter := rd.HScan(rd.Ctx, key, 0, "", 10).Iterator()
	for iter.Next(rd.Ctx) {
		tmpli = append(tmpli, iter.Val())
	}
	result = make(map[string]int64)
	for i := 0; i < len(tmpli); i += 2 {
		result[tmpli[i]] = common.NewString(tmpli[i+1]).ToInt64V()
	}
	return result, nil
}

// Args 是 Redis 命令参数的别名类型
type Args = map[string]interface{}
